/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package technotheist.geo.noise;

import com.jme3.math.FastMath;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import technotheist.geo.map.Map;
import static technotheist.geo.map.MapUtil.PANELS;

/**
 *
 * @author Mitch
 */
public class FractalNoise {

    public static void noise(float min, float max, float offset, float roughness, Map map, int component) {
        TIntSet traversed = new TIntHashSet();

        for (int p = 0; p < PANELS; p++) {
            int len = map.sideLength;
            int i00 = map.index(p, 0, 0);
            int i10 = map.index(p, 0 + len, 0);
            int i01 = map.index(p, 0, 0 + len);
            int i11 = map.index(p, 0 + len, 0 + len);

            float f00 = traversed.contains(i00) ? map.getFloat(i00 * map.components + component) : FastMath.nextRandomFloat() * (max - min) + min;
            float f10 = traversed.contains(i00) ? map.getFloat(i10 * map.components + component) : FastMath.nextRandomFloat() * (max - min) + min;
            float f01 = traversed.contains(i00) ? map.getFloat(i01 * map.components + component) : FastMath.nextRandomFloat() * (max - min) + min;
            float f11 = traversed.contains(i00) ? map.getFloat(i11 * map.components + component) : FastMath.nextRandomFloat() * (max - min) + min;

            traversed.add(i00);
            traversed.add(i10);
            traversed.add(i01);
            traversed.add(i11);

            map.setFloat(i00 * component, f00);
            map.setFloat(i10 * component, f10);
            map.setFloat(i01 * component, f01);
            map.setFloat(i11 * component, f11);

            noise(min, max, offset, roughness, map, traversed, p, 0, 0, len, component);
        }
    }

    private static void noise(float min, float max, float offset, float roughness, Map map, TIntSet traversed,
            int p, int x0, int y0, int len, int component) {


        int i00 = map.index(p, x0, y0);
        int i10 = map.index(p, x0 + len, y0);
        int i01 = map.index(p, x0, y0 + len);
        int i11 = map.index(p, x0 + len, y0 + len);

        float f00 = map.getFloat(i00 * component);
        float f10 = map.getFloat(i10 * component);
        float f01 = map.getFloat(i01 * component);
        float f11 = map.getFloat(i11 * component);

        int h = len / 2;

        int i0h = map.index(p, x0, y0 + h);
        int i1h = map.index(p, x0 + len, y0 + h);
        int ihh = map.index(p, x0 + h, y0 + h);
        int ih0 = map.index(p, x0 + h, y0);
        int ih1 = map.index(p, x0 + h, y0 + h);

        float f0h = traversed.contains(i0h) ? map.getFloat(i0h * map.components + component)
                : 0.5f * (f00 + f01) + offset * (2 * FastMath.nextRandomFloat() - 1);
        float f1h = traversed.contains(i0h) ? map.getFloat(i1h * map.components + component)
                : 0.5f * (f10 + f11) + offset * (2 * FastMath.nextRandomFloat() - 1);
        float fh0 = traversed.contains(ih0) ? map.getFloat(ih0 * map.components + component)
                : 0.5f * (f00 + f10) + offset * (2 * FastMath.nextRandomFloat() - 1);
        float fh1 = traversed.contains(ih1) ? map.getFloat(ih1 * map.components + component)
                : 0.5f * (f01 + f11) + offset * (2 * FastMath.nextRandomFloat() - 1);
        float fhh = ((f00 + f11 + fh0 + fh1 + f0h + f1h) / 6) + offset * (2 * FastMath.nextRandomFloat() - 1);
        traversed.add(i0h);
        traversed.add(i1h);
        traversed.add(ih0);
        traversed.add(ih1);
        traversed.add(ihh);
        map.setFloat(i0h*map.components+component, f0h);
        map.setFloat(i1h*map.components+component, f1h);
        map.setFloat(ih0*map.components+component, fh0);
        map.setFloat(ih1*map.components+component, fh1);
        map.setFloat(ihh*map.components+component, fhh);
        noise(min, max, offset*roughness, roughness, map, traversed, p, x0, y0, h, component);
        noise(min, max, offset*roughness, roughness, map, traversed, p, x0+h, y0, h, component);
        noise(min, max, offset*roughness, roughness, map, traversed, p, x0, y0+h, h, component);
        noise(min, max, offset*roughness, roughness, map, traversed, p, x0+h, y0+h, h, component);
    }
}
